﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using VirtualTournaments.Business.Tournaments.Exceptions;

namespace VirtualTournaments.Business.Tournaments
{
    public class PlayOffTournament : Tournament
    {
        private TournamentState _state;

        private static List<int> VALID_TEAMS_NUMBER = new List<int>(new int[] { 4, 8, 16, 32 });

        public PlayOffTournament(IEnumerable<Team> teams)
            : this(null, teams, MatchWithPenaltiesFactory.GetInstance())
        { }

        public PlayOffTournament(String name, IEnumerable<Team> teams)
            : this(name, teams, MatchWithPenaltiesFactory.GetInstance())
        { }

        public PlayOffTournament(String name, IEnumerable<Team> teams, MatchWithWinnerFactory factory) :
            base(name, teams, factory)
        {
            if (!VALID_TEAMS_NUMBER.Contains(Teams.Count()))
                throw new InvalidTeamsNumberException();
            if (this.Fixture == null)
            {
                this.Fixture = new Fixture(this);
                Fixture.GenerateJourney(teams, factory);
            }
            State = new FirstRounds(this);
        }

        protected TournamentState State
        {
            get { return _state; }
            set { _state = value; }
        }

        public override void PlayNextJourney()
        {
            State.PlayJourney(Fixture.GetJourney(++LastJourneyNumber));
        }

        protected abstract class TournamentState
        {
            private PlayOffTournament _tournament;

            public TournamentState(PlayOffTournament tournament)
            {
                _tournament = tournament;
            }

            public PlayOffTournament Tournament
            {
                get { return _tournament; }
            }

            public abstract void PlayJourney(Journey presentJourney);
        }

        protected class FirstRounds : TournamentState
        {
            public FirstRounds(PlayOffTournament tournament)
                : base(tournament)
            {
                if (tournament.Teams.Count() == 4)
                    tournament.State = new Semifinals(tournament);
            }

            public override void PlayJourney(Journey presentJourney)
            {
                foreach (Match match in presentJourney)
                    Tournament.TeamsList.Remove(match.Play().GetLoser());
                Tournament.Fixture.GenerateJourney(Tournament.Teams, Tournament.Factory);
                if (Tournament.Teams.Count() == 4)
                    Tournament.State = new Semifinals(Tournament);
            }
        }

        protected class Semifinals : TournamentState
        {
            public Semifinals(PlayOffTournament tournament) : base(tournament) { }

            public override void PlayJourney(Journey presentJourney)
            {
                List<Team> winners = new List<Team>();
                List<Team> lossers = new List<Team>();
                foreach (Match Match in presentJourney)
                {
                    MatchResult result = Match.Play();
                    winners.Add(result.GetWinner());
                    lossers.Add(result.GetLoser());
                }
                Tournament.Fixture.GenerateJourney(lossers, Tournament.Factory);
                Tournament.Fixture.GenerateJourney(winners, Tournament.Factory);
                Tournament.State = new ThirdPlace(Tournament);
            }
        }

        protected class ThirdPlace : TournamentState
        {
            public ThirdPlace(PlayOffTournament tournament) : base(tournament) { }

            public override void PlayJourney(Journey presentJourney)
            {
                Tournament.Third = presentJourney.First().Play().GetWinner();
                Tournament.State = new Final(Tournament);
            }
        }

        protected class Final : TournamentState
        {
            public Final(PlayOffTournament tournament) : base(tournament) { }

            public override void PlayJourney(Journey presentJourney)
            {
                MatchResult result = presentJourney.First().Play();
                Tournament.Second = result.GetLoser();
                Tournament.First = result.GetWinner();
                Tournament.IsFinished = true;
                Tournament.State = new Finished(Tournament);
            }
        }

        protected class Finished : TournamentState
        {
            public Finished(PlayOffTournament tournament) : base(tournament) { }

            public override void PlayJourney(Journey presentJourney)
            {
                throw new FinishedTournamentException();
            }
        }

    }
}
